# -*- coding: utf-8 -*-
import logging
import os
import sys
import json
import contextlib
import platform
import subprocess
import imp
import re
import filecmp
import hashlib
import shutil
import getpass
import tempfile
from distutils.dir_util import copy_tree
from datetime import datetime
from Qt.QtWidgets import *
from Qt.QtCore import *
from Qt.QtGui import *
from liberPackages import yaml
import package
import liberConfig
from Temporary import Temporary
from liberPackages.dayu_path import DayuPath


def user():
    """
    :return: current user
    """
    return getpass.getuser()


def now():
    """
    :return: current date
    """
    return datetime.now().strftime("%Y-%m-%d")


def read_json(path):
    """
    parser .json file
    :param path: a json file path <str>
    :return:
    """
    if os.path.isfile(path):
        try:
            with open(path, 'r') as f:
                data = json.loads(f.read())
                return data
        except:
            return dict()
    return False


def write_json(path, data):
    """
    write data to a .json file
    :param path: a json file path <str>
    :param data: <list> or <str> or <dict>
    :return:
    """
    d = os.path.dirname(path)
    if not os.path.isdir(d):
        os.makedirs(d)
    with open(path, 'w') as f:
        json_data = json.dumps(data)
        f.write(json_data)


def read_yaml(path):
    """
    read yml data from path
    :param path: a yml path <str>
    :return:
    """
    if not os.path.isfile(path):
        return
    with open(path, "r") as f:
        data = yaml.load(f)
        return data


def write_yaml(yaml_path, data):
    """
    write data to a .yml file
    :param yaml_path: a json file path <str>
    :param data: <list> or <str> or <dict>
    :return:
    """
    yaml_dir = os.path.dirname(yaml_path)
    if not os.path.isdir(yaml_dir):
        os.makedirs(yaml_dir)
    with open(yaml_path, "w") as f:
        yaml.dump(data, f)


def system():
    """
    :rtype: str
    """
    return platform.system().lower()


def isMac():
    """
    :rtype: bool
    """
    return system().startswith('mac') or system().startswith('os') or system().startswith('darwin')


def isWindows():
    """
    :rtype: bool
    """
    return system().startswith('win')


def isLinux():
    """
    :rtype: bool
    """
    return system().startswith('lin')


def open_location(path):
    """
    :type path: str
    :rtype: None
    """
    if isLinux():
        os.system('konqueror "%s"&' % path)
    elif isWindows():
        os.startfile('%s' % path)
    elif isMac():
        subprocess.call(['open', '-R', path])


@contextlib.contextmanager
def render_ui():
    """
    show a Qt widget
    :return:
    """
    app = None
    is_app_running = QApplication.instance()
    if not is_app_running:
        app = QApplication(sys.argv)
    yield
    if app:
        sys.exit(app.exec_())


def load_module(name, paths):
    """
    :param name: module name
    :param paths:directory <str> or <list>
    :return:
    """
    if isinstance(paths, basestring):
        paths = [paths]
    try:
        fn_, path, desc = imp.find_module(name, paths)
        mod = imp.load_module(name, fn_, path, desc)
        return mod
    except ImportError as e:
        logging.error(str(e))


def load_hook(name):
    """
    :param name: module name<str>
    :return:
    """
    hook_dir = liberConfig.HOOK_DIR
    liber_hook_dir = package.get("liberHook")
    hook_dir.append(liber_hook_dir)
    mod = load_module(name, hook_dir)
    return mod


def scaled_pixmap_to_label(pixmap, label):
    """
    :param pixmap: QPixmap
    :param label: QLabel
    :return:
    """
    label_width = label.width()
    label_height = label.height()
    image_width = pixmap.width()
    image_height = pixmap.height()
    if image_width > image_height:
        scaled = pixmap.scaled(QSize(label_width, image_width / label_width * image_height),
                               Qt.KeepAspectRatio, Qt.SmoothTransformation)
    elif image_width < image_height:
        scaled = pixmap.scaled(QSize(image_height / label_height * image_width, label_height),
                               Qt.KeepAspectRatio, Qt.SmoothTransformation)
    else:
        scaled = pixmap.scaled(QSize(label_width, label_height),
                               Qt.KeepAspectRatio, Qt.SmoothTransformation)
    return scaled


def pixmap_resize_to_label(pixmap, label):
    """
    :param pixmap: QPixmap
    :param label: QLabel
    :return:
    """
    label.setPixmap(pixmap)
    label.setAlignment(Qt.AlignCenter)


def create_round_thumbnail(image_path, scale_size):
    """
    Create a custom px wide circle thumbnail
    :param image_path: a png file path
    :param scale_size: scale size of the image
    :return: QPixmap
    """
    # get the 512 base image
    base_image = QPixmap(scale_size, scale_size)
    base_image.fill(Qt.transparent)
    # now attempt to load the image
    image = QImage(image_path)
    thumb = QPixmap.fromImage(image)
    # pixmap will be a null pixmap if load fails
    if not thumb.isNull():
        # scale it down to fit inside a frame of maximum 512x512
        thumb_scaled = thumb.scaled(scale_size,
                                    scale_size,
                                    # Qt.KeepAspectRatioByExpanding,
                                    Qt.IgnoreAspectRatio,
                                    Qt.SmoothTransformation)

        # now composite the thumbnail on top of the base image
        # bottom align it to make it look nice
        thumb_img = thumb_scaled.toImage()
        brush = QBrush(thumb_img)
        painter = QPainter(base_image)
        painter.setRenderHint(QPainter.Antialiasing)
        painter.setBrush(brush)
        painter.setPen(Qt.NoPen)
        painter.drawEllipse(0, 0, scale_size, scale_size)
        painter.end()

    return base_image


def frame_padding(file_name, ext):
    """
    get frame padding of a file
    :param file_name:
    :param ext: file ext
    :return:
    """
    padding = 0
    pattern = ".*\D(\d+)\.%s" % ext
    padding_list = re.findall(pattern, file_name)
    if padding_list:
        padding = len(padding_list[0])
    return padding


def copy_file(src, dst):
    """
    :param src: source file
    :param dst: destination file
    :return:
    """
    dst_dir = os.path.dirname(dst)
    if not os.path.isdir(dst_dir):
        os.makedirs(dst_dir)
    shutil.copyfile(src, dst)


def copy(src, dst):
    """
    :param src: source file
    :param dst: destination file
    :return:
    """
    dst_dir = os.path.dirname(dst)
    if (not src) or (not os.path.isfile(src)):
        print "%s is not an exist file" % src
        return
    if os.path.exists(dst):
        if filecmp.cmp(src, dst):
            print "%s and %s is the same." % (src, dst)
            return
    if not os.path.isdir(dst_dir):
        os.makedirs(dst_dir)
    try:
        shutil.copyfile(src, dst)
        return True
    except:
        print "Can't copy %s--->%s" % (src, dst)
        return False


def copytree(src, dst):
    copy_tree(src, dst)


def resize_image_with_oiio(src_image_path, dst_image_path, ratio=0.5):
    from liberPackages.OpenImageIO import OpenImageIO as oiio
    dst_image_dir = os.path.dirname(dst_image_path)
    if not os.path.isdir(dst_image_dir):
        os.makedirs(dst_image_dir)
    input_image = oiio.ImageInput.open(src_image_path)
    if not input_image:
        print 'Could not open %s "' % input_image
        print "\tError: ", oiio.geterror()
        return
    image_spec = input_image.spec()
    bit = image_spec.format
    channel_num = image_spec.nchannels
    buf_src = oiio.ImageBuf(src_image_path)
    obj = oiio.ImageSpec(int(image_spec.width * ratio), int(image_spec.height * ratio), channel_num, bit)
    # obj.attribute("tiff.ColorSpace", "sRGB")
    dst = oiio.ImageBuf(obj)
    oiio.ImageBufAlgo.resize(dst, buf_src)
    dst.write(dst_image_path)
    dst.clear()
    buf_src.clear()
    input_image.close()


def resize_image(src_image_path, dst_image_path, ratio=0.2):
    """
    resize a image, default scale ration is 0.5
    :param src_image_path: source image path
    :param dst_image_path: destination image path
    :param ratio: scale ratio
    :return:
    """
    ffmpeg_path = "%s/%s" % (package.get("liberPackages"), "ffmpeg.exe")
    ext = os.path.splitext(src_image_path)[-1]
    if ext in [".hdr"]:
        with Temporary(suffix=".exr", mode="mktemp") as tmp:
            resize_image_with_oiio(src_image_path, tmp)
            resize_image(tmp, dst_image_path)
    else:
        if ext in [".exr"]:
            cmd = "\"{0}\" -apply_trc iec61966_2_1 -i \"{1}\" -vf scale=256:ih/iw*256 -y \"{2}\"" \
                .format(ffmpeg_path, src_image_path, dst_image_path)
        else:
            cmd = "\"{0}\" -i \"{1}\" -vf scale=256:ih/iw*256 -y \"{2}\"".format(ffmpeg_path, src_image_path,
                                                                                 dst_image_path)
        p = subprocess.Popen(cmd, shell=True)
        p.wait()


def save_pixmap(pixmap, thumbnail_path):
    if pixmap.isNull():
        return
    with Temporary(suffix=".png", mode="mktemp") as tmp:
        pixmap.save(tmp)
        resize_image(tmp, thumbnail_path)
        return True


def capture_thumbnail(src_video, thumbnail_path):
    """
    capture a frame of video as thumbnail
    :param src_video:
    :param thumbnail_path:
    :return:
    """
    ffmpeg_path = "%s/%s" % (package.get("liberPackages"), "ffmpeg.exe")
    cmd = "\"%s\" -ss 00:00:01 -i \"%s\" -frames:v 1 -y \"%s\"" % (ffmpeg_path, src_video, thumbnail_path)
    p = subprocess.Popen(cmd, shell=True)
    p.wait()
    if os.path.isfile(thumbnail_path):
        resize_image(thumbnail_path, thumbnail_path)


def convert_sequence_to_gif(file_pattern, start_number, gif_path):
    """
    convert a sequence to gif
    Args:
        file_pattern: xxxx.%04d.ext
        start_number: 1001
        gif_path: xxxx.gif

    Returns:
    """
    gif_path = gif_path.replace("\\", "/")
    ffmpeg_path = "{}/{}".format(package.get("liberPackages"), "ffmpeg.exe")
    cmd = "\"%s\" -start_number %s -i \"%s\" -y -vf scale=256:ih/iw*256 -r 15 \"%s\" " % (ffmpeg_path, start_number, file_pattern, gif_path)
    print cmd
    p = subprocess.Popen(cmd, shell=True)
    p.wait()


def convert_to_gif(src_video, gif_path):
    """
    convert video to gif
    Args:
        src_video: a video file
        gif_path: gif path

    Returns:
    """
    src_video = src_video.replace("\\", "/")
    gif_path = gif_path.replace("\\", "/")
    ffmpeg_path = "{}/{}".format(package.get("liberPackages"), "ffmpeg.exe")
    cmd = "\"%s\" -i \"%s\" -y -vf scale=256:ih/iw*256 -r 15 \"%s\"" % (ffmpeg_path, src_video, gif_path)
    p = subprocess.Popen(cmd, shell=True)
    p.wait()


def parse_sequence_file(files):
    """
    Args:
        files: a file list.
    Returns:
        str: if sequence, return (xxx.%04d.ext, [1001, 1002, 1003....])
            else: return (files[0], [])
    """
    first_file = files[0]
    scaned = DayuPath(first_file).scan()
    dayu_path_list = list(scaned)
    dayu_object = dayu_path_list[0]
    file_ = dayu_object.replace("\\", "/")
    frames = dayu_object.frames
    if frames and len(frames) > 1:
        return str(file_), frames
    return str(first_file).replace("\\", "/"), []


def automatic_thumbnail(files, thumbnail_path):
    """
    generate a thumbnail of a video
    automatic create a thumbnail picture
    :param files: a file list
    :param thumbnail_path: thumbnail path
    :return:
    """
    _file, frames = parse_sequence_file(files)
    ext = os.path.splitext(_file)[-1]
    gif_path = os.path.splitext(thumbnail_path)[0] + ".gif"
    if ext in [".mov", ".mp4", ".avi"]:
        capture_thumbnail(_file, thumbnail_path)
        convert_to_gif(_file, gif_path)
        return
    if re.findall("%\d+d", _file):  # %04d in _file
        try:
            first_frame = frames[0]
            convert_sequence_to_gif(_file, first_frame, gif_path)
        except:
            print "Can not convert %s to gif" % _file
    file_num = len(files)
    file_index = file_num/2
    middle_file = files[file_index]
    resize_image(middle_file, thumbnail_path)


def save_info(comments, info_path):
    """
    :param comments: str.
    :param info_path: json file path.
    :return:
    """
    info = dict(created_by=user(),
                created_at=now(),
                comments=comments)
    write_json(info_path, info)


def get_md5(value):
    """
    :param value: str
    :return:
    """
    hl = hashlib.md5()
    hl.update(value.encode(encoding='utf-8'))
    return hl.hexdigest()


def temp_file(file_name):
    temp_dir = tempfile.gettempdir()
    file_ = os.path.join(temp_dir, file_name).replace("\\", "/")
    return file_


def get_texture_abs_path(texture):
    real_texture = list()
    if not texture:
        return []
    texture = texture.replace("\\", "/")
    if texture.startswith("$"):
        _list = texture.split("/")
        prefix = _list[0]
        env = prefix.split("$")[-1]
        env = env.strip("{").strip("}")
        suffix = "/".join(_list[1:])
        env_path = os.environ.get(env)
        if not env_path:
            return []
        texture = "{0}/{1}".format(env_path, suffix)
    if "<udim>" in texture or "<UDIM>" in texture:
        texture_dir, texture_base_name = os.path.split(texture)
        pattern = texture_base_name.replace("<udim>", "\d{4}")
        pattern = pattern.replace("<UDIM>", "\d{4}")
        if os.path.isdir(texture_dir):
            for i in os.listdir(texture_dir):
                if re.match(pattern, i):
                    full_name = "%s/%s" % (texture_dir, i)
                    full_name = full_name.replace("\\", "/")
                    real_texture.append(full_name)
    elif os.path.isfile(texture):
        real_texture.append(texture)
    else:
        logging.warning("%s is not an exist file." % texture)
    return real_texture


if __name__ == "__main__":
    dir_ = r"D:\sequence"
    files = [os.path.join(dir_, f).replace("\\", "/") for f in os.listdir(dir_)]
    print parse_sequence_file(files)
